﻿// -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	SmartLib
//  File name:	SCache.cs
//  Developer:	Igor Solomatov
//  Created:	12.11.2012
// 
// -------------------- iSharePoint -------------------- //

using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;

namespace Smart
{
	/// <summary>
	/// Smart cache
	/// </summary>
	public static class SCache
	{
		[ThreadStatic]
		private static Cache _cache;

		/// <summary>
		/// Returns cache
		/// </summary>
		public static Cache GetCache()
		{
			var cache = _cache;

			if (cache == null)
			{
				try { _cache = cache = HttpRuntime.Cache; }
				catch { cache = null; }
			}

			if (cache == null)
			{
				var ctx = HttpContext.Current;

				_cache = cache = ctx != null ? ctx.Cache : null;
			}

			return cache;
		}

		static SCache()
		{
			_cache = null;
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>
		/// </summary>
		public static TValueType Get<TValueType>(string key)
		{
			return GetCache().SafeGet<TValueType>(key);
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>
		/// </summary>
		public static TValueType Get<TValueType>(string key, TValueType defaultValue)
		{
			return GetCache().SafeGet(key, defaultValue);
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>, uses <paramref name="loader"/> to add non existing value
		/// </summary>
		public static TValueType Get<TValueType>(string key, TimeSpan absoluteExpiration, Func<string, TValueType> loader)
		{
			return GetCache().SafeGet(key, absoluteExpiration, loader);
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>, uses <paramref name="loader"/> to add non existing value
		/// </summary>
		public static TValueType Get<TValueType>(string key, TimeSpan absoluteExpiration, TimeSpan slidingExpiration, Func<string, TValueType> loader)
		{
			return GetCache().SafeGet(key, absoluteExpiration, slidingExpiration, loader);
		}

		/// <summary>
		/// Sets <paramref name="value"/> by <paramref name="key"/> in cache
		/// </summary>
		public static void Set<TValueType>(string key, TValueType value, TimeSpan absoluteExpiration)
		{
			GetCache().SafeSet(key, value, absoluteExpiration);
		}

		/// <summary>
		/// Sets <paramref name="value"/> by <paramref name="key"/> in cache
		/// </summary>
		public static void Set<TValueType>(string key, TValueType value, TimeSpan absoluteExpiration, TimeSpan slidingExpiration)
		{
			GetCache().SafeSet(key, value, absoluteExpiration, slidingExpiration);
		}

		/// <summary>
		/// Sets <paramref name="value"/> by <paramref name="key"/> in cache
		/// </summary>
		public static void Set<TValueType>(string key, TValueType value, DateTime absoluteExpiration, TimeSpan slidingExpiration)
		{
			GetCache().SafeSet(key, value, absoluteExpiration, slidingExpiration);
		}

		public static void ClearAllKeysStartsWith(string keyStart)
		{
			var cache = GetCache();

			var keys = new List<string>();

			foreach (DictionaryEntry entry in cache)
			{
				var key = entry.Key.ToString();

				if (key.StartsWith(keyStart, StringComparison.OrdinalIgnoreCase))
					keys.Add(key);
			}

			foreach (var key in keys)
			{
				cache.Remove(key);
			}
		}
	}

	/// <summary>
	/// Smart cache
	/// </summary>
	public class SCache<TValueType>
	{
		[NonSerialized]
		private Cache _cache;

		public Cache Cache
		{
			get { return _cache ?? (_cache = SCache.GetCache()); }
		}

		public TimeSpan AbsoluteExpiration { get; set; }

		/// <summary>
		/// Smart cache
		/// </summary>
		/// <param name="absoluteExpiration">default expiration timeout</param>
		public SCache(TimeSpan absoluteExpiration)
		{
			AbsoluteExpiration = absoluteExpiration;
		}

		/// <summary>
		/// Gets or Sets value in cache by <paramref name="key"/>
		/// </summary>
		public TValueType this[string key]
		{
			get { return Cache.SafeGet<TValueType>(key); }
			set { Cache.SafeSet(key, value, AbsoluteExpiration); }
		}

		/// <summary>
		/// Sets <paramref name="value"/> by <paramref name="key"/> in cache
		/// </summary>
		public TValueType this[string key, TimeSpan absoluteExpiration]
		{
			set { Cache.SafeSet(key, value, absoluteExpiration); }
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>, uses <paramref name="loader"/> to add non existing value
		/// </summary>
		public TValueType this[string key, Func<string, TValueType> loader]
		{
			get { return Cache.SafeGet(key, AbsoluteExpiration, loader); }
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>, uses <paramref name="loader"/> to add non existing value
		/// </summary>
		public TValueType this[string key, TimeSpan absoluteExpiration, Func<string, TValueType> loader]
		{
			get { return Cache.SafeGet(key, absoluteExpiration, loader); }
		}

		/// <summary>
		/// Returns value from cache by <paramref name="key"/>, uses <paramref name="loader"/> to add non existing value
		/// </summary>
		public TValueType this[string key, TimeSpan absoluteExpiration, TimeSpan slidingExpiration, Func<string, TValueType> loader]
		{
			get { return Cache.SafeGet(key, absoluteExpiration, slidingExpiration, loader); }
		}

		public void ClearAllKeysStartsWith(string keyStart)
		{
			var cache = Cache;

			var keys = new List<string>();

			foreach (DictionaryEntry entry in cache)
			{
				var key = entry.Key.ToString();

				if (key.StartsWith(keyStart, StringComparison.OrdinalIgnoreCase))
					keys.Add(key);
			}

			foreach (var key in keys)
			{
				cache.Remove(key);
			}
		}
	}
}